#pragma once

#include "Common.hpp"
#include "PageCache.hpp"

//����ģʽ������ģʽ
class CentralCache
{
public:
	static CentralCache* GetInstance()
	{
		return &_sInst;
	}

	//�����Ļ����л�ȡһ�������Ķ����thread cache
	//������ǰ��������Ͳ�����������ȡ���ٸ������ĸ���ÿ���ռ�Ĵ�С, ����ֵ��ʵ���ϸ��˶��ٸ��ռ�
	size_t FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
	{
		size_t index = SizeClass::Index(size);   //�ҵ���Ҫ��һ��Ͱ��
		
		_spanLists[index].GetMtx().lock();	
		Span* span = GetOneSpan(_spanLists[index], size);
		assert(span);
		assert(span->_freeList); //���ﲻΪ�գ���˵����span�����пռ���Է����ȥ
		start = span->_freeList;
		end = start;
		
		size_t i = 0;
		size_t actumalNum = 1;
		while (i < batchNum - 1 && NextObj(end) != nullptr)
		{
			end = NextObj(end);
			++i;
			++actumalNum;
		}

		span->_freeList = NextObj(end);
		NextObj(end) = nullptr;
		span->_useCount += actumalNum;
		_spanLists[index].GetMtx().unlock();

		return actumalNum;
	}

	//��ȡһ���ǿյ�Span
	Span* GetOneSpan(SpanList& list, size_t size)
	{
		//CentralCache������Ҫ�ǿյ�Span
		Span* it = list.Begin();
		while (it != list.End())
		{
			if (it->_freeList != nullptr)
			{
				return it;
			}
			it = it->_next;
		}

		//�Ȱ�central cache��Ͱ�������������������̻߳��ڴ�ʱ,��������
		list.GetMtx().unlock();
		//û�п��е�Span�ˣ���PageCacheҪ�ռ�
		PageCache::GetInstance()->GetMtx().lock();
		Span* span = PageCache::GetInstance()->NewSpan(UnitClass::NumMovePage(size));
		span->_isUse = true;
		span->_objSize = size;
		PageCache::GetInstance()->GetMtx().unlock();
		//�õ��ڴ�󣬲���Ҫ�ڼ�Ͱ������Ϊû�йҵ�spanList�У����������̷߳��ʲ���
		//span����ʼ���յ��ַ���ڴ���ֽ�����С
		char* start = (char*)(span->_pageId << PAGE_SHIFT);
		size_t bytes = span->_n << PAGE_SHIFT;
		char* end = start + bytes;

		//�Ѵ���ڴ��г�����������������
		span->_freeList = start;
		start += size;
		void* tail = span->_freeList;

		while (start < end)
		{
			NextObj(tail) = start;
			tail = start;
			start += size;
		}
		NextObj(tail) = nullptr;
		
		//�к�span�Ժ���Ҫ��span�ҵ�Ͱ�����ʱ���ڼ���
		list.GetMtx().lock();
		list.PushFront(span);
		return span;
	}

	//��һ�������Ķ����ͷŵ�span���
	void ReleaseListToSpans(void* start, size_t size)
	{
		size_t index = SizeClass::Index(size);
		_spanLists[index].GetMtx().lock();
		while (start)
		{
			void* next = NextObj(start);
			Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
			NextObj(start) = span->_freeList;
			span->_freeList = start;

			start = next;
			span->_useCount--;
			//���˵��span���зֳ�ȥ������С���ڴ涼������
			//�Ϳ��Է��ظ�PageCache, PageCache�Ϳ��Կ��Ǻϲ�span
			if (span->_useCount == 0)
			{
				_spanLists[index].Erase(span);
				span->_freeList = nullptr;
				span->_next = nullptr;
				span->_prev = nullptr;
			
				//�Ƚ��Ͱ�����������������߳̿������������������ڴ�
				_spanLists[index].GetMtx().unlock();
				PageCache::GetInstance()->GetMtx().lock();
				PageCache::GetInstance()->ReleaseSpanToRageCache(span);
				PageCache::GetInstance()->GetMtx().unlock();
				_spanLists[index].GetMtx().lock();
			}
		}
		_spanLists[index].GetMtx().unlock();
	}
private:
	SpanList _spanLists[NFREELISTS];

private:
	CentralCache() {}
	CentralCache(const CentralCache&) = delete;
	static CentralCache _sInst;
};

CentralCache CentralCache::_sInst;